﻿#include "pch.h"
#include "sbcs.h"
#ifdef _MSC_VER
#include <intrin.h>
#endif

//////////////////////////////////////////////////////////////////////////
// spin lock

// ARM 배리어
#if defined(__GNUC__) && ((_SB_ANDROID_ && _SB_ARM_V7_) || (!_SB_ANDROID_ && _SB_ARM_))
#if _SB_ARM_V7_
#	define K_ARM_BARRIER "dmb"
#else // arch6
#	define K_ARM_BARRIER "mcr p15, 0, r0, c7, c10, 5"
#endif
#endif

/**
 * 잠궈 보기를 수행함.
 * @param [입력]	self	수행에 대한 자기 자신 값 (=this)
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_lock_try(kLock* self)
{
#ifdef K_ARM_BARRIER
	int r;

	// [2013-12-10 ksh] 일단... ldrex는 요즘 ARM 모바일에서는 다 지원하는 듯함.
	// [2014-10-8 ksh] 근데, 안드로이드는 
	//   "APP_ABI := armeabi-v7a"
	//   "LOCAL_ARM_MODE := arm"
	//   이렇게 두가지 상태가 되야 되드라...
	__asm__ __volatile__(
		"ldrex %0, [%2]; \n"
		"cmp %0, %1; \n"
		"strexne %0, %1, [%2]; \n"
		K_ARM_BARRIER : "=&r"(r) : "r"(1), "r"(self) : "memory", "cc");

	return r == 0;
#elif _SB_PTHREAD_ && !defined(__GNUC__)
	return pthread_mutex_trylock(self) == 0;
#else
	volatile long m = k_atomexch(self, 1);
	KSAFE_BARRIER;
	return m == 0;
#endif
}

/**
 * 잠그기에 들어감.
 * @param [입력]	self	수행에 대한 자기 자신 값 (=this)
 */
void k_lock_enter(kLock* self)
{
#ifdef K_ARM_BARRIER
	kint i;
	for (i = 0; !k_lock_try(self); i++)
		k_yield(i);
#elif _SB_PTHREAD_ && !defined(__GNUC__)
	pthread_mutex_lock(self);
#else
	kint i;
	for (i = 0; !k_lock_try(self); i++)
		k_yield(i);
#endif
}

/**
 * 잠그기에서 나옴.
 * @param [입력]	self	수행에 대한 자기 자신 값 (=this)
 */
void k_lock_leave(kLock* self)
{
#ifdef K_ARM_BARRIER
	__asm__ __volatile__(K_ARM_BARRIER ::: "memory");
	*self = 0;
#elif _SB_PTHREAD_ && !defined(__GNUC__)
	pthread_mutex_unlock(self);
#else
	KSAFE_BARRIER;
	*(volatile kint*)self = 0;
#endif
}


//////////////////////////////////////////////////////////////////////////
// yield, from BOOST yield

//
//  yield_k.hpp
//
//  Copyright (c) 2008 Peter Dimov
//
//  void yield( unsigned k );
//
//  Typical use:
//
//  for( unsigned k = 0; !try_lock(); ++k ) yield( k );
//
//  Distributed under the Boost Software License, Version 1.0.
//  See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt
//

/**
 * 지정한 스텝에 따라 스레드 또는 프로세스의 양보를 수행.
 * @param	step	증가 분량.
 */
void k_yield(kint step)
{
	if (step < 4)
	{
	}
#ifdef KSAFE_PAUSE
	else if (step < 16)
	{
		KSAFE_PAUSE;
	}
#endif
#if _SB_PTHREAD_
	else if (step < 32 || (step & 1))
	{
		sched_yield();
	}
	else
	{
#if 0
		k_msleep(1);
#else
		struct timespec ts;
		ts.tv_sec = 0;
		ts.tv_nsec = 1000;
		nanosleep(&ts, NULL);
#endif
	}
#else
	else if (step < 32)
	{
		k_sleep(0);
	}
	else
	{
		k_sleep(1);
	}
#endif
}
